﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using Common.Utilities.Dispatcher;

namespace WindowsUserControl.Dialogs
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="assembly"></param>
    /// <param name="classType"></param>
    /// <param name="method"></param>
    /// <param name="parameterBindings"></param>
    public delegate void MethodBound(
        Assembly assembly, Type classType, MethodInfo method, Dictionary<string, string> parameterBindings);

    /// <summary>
    /// 
    /// </summary>
    public partial class MethodBinder : Form
    {
        /// <summary>
        /// 
        /// </summary>
        public event MethodBound OnMethodBound;

        private Dictionary<string, GlobalVariable> _GlobalVars;
        private Assembly _Assembly;
        private Type _InterfaceType;
        private string _ClassName;
        private string _SelectedMethodName;
        private Dictionary<string, string> _ParameterBindings;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="globalVars"></param>
        public MethodBinder(GlobalVariable[] globalVars)
        {
            InitializeComponent();
            this._GlobalVars=new Dictionary<string, GlobalVariable>();
            if(globalVars !=null && globalVars.Length>0)
            {
                foreach(GlobalVariable gVar in globalVars)
                {
                    if(!this._GlobalVars.ContainsKey(gVar.VarName))
                    {
                        this._GlobalVars.Add(gVar.VarName,gVar);
                    }
                }
            }
        }

        private void MethodBinder_Load(object sender, EventArgs e)
        {
            this._Assembly = Assembly.GetExecutingAssembly();
        }

        #region props
        /// <summary></summary>
        public string AssemblyFilePath
        {
            get
            {
                if(this._Assembly !=null)
                    return this._Assembly.Location;
                else
                    return string.Empty;
            }
            set
            {
                if(!string.IsNullOrEmpty(value))
                {
                    this._Assembly = Assembly.LoadFrom(value);
                }
            }
        }
        /// <summary></summary>
        public Type InterfaceType
        {
            get
            {
                return this._InterfaceType;
            }
            set
            {
                this._InterfaceType = value;
            }
        }
        /// <summary></summary>
        public string ClassName
        {
            get
            {
                return this._ClassName;
            }
            set
            {
                this._ClassName = value;
            }
        }
        /// <summary></summary>
        public string MethodName
        {
            get
            {
                return this._SelectedMethodName;
            }
            set
            {
                this._SelectedMethodName = value;
            }
        }
        /// <summary></summary>
        public Dictionary<string,string> ParameterBindings
        {
            get
            {
                return this._ParameterBindings;
            }
            set
            {
                this._ParameterBindings = value;
            }
        }
        /// <summary></summary>
        public Dictionary<string, GlobalVariable> GlobalVars
        {
            get { return _GlobalVars; }
            set { _GlobalVars = value; }
        }

        #endregion

        #region display
        /// <summary></summary>
        public void UpdateDisplay()
        {
            this.txt_AssemblyFilePath.Text = this.AssemblyFilePath;
            this.PopulateClasses();
            this.PopulateMethods();
            this.PopulateParameterBindings();
        }

        private void cbo_Methods_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(this.cbo_Methods.SelectedIndex>=0 && this._Assembly !=null && (!string.IsNullOrEmpty(this._ClassName)))
            {
                this._SelectedMethodName = this.cbo_Methods.SelectedItem.ToString();
                this.PopulateParameterBindings();
            }
        }

        private void cbo_Classes_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(this._Assembly !=null && this.cbo_Classes.SelectedIndex >=0)
            {
                this._ClassName = this.cbo_Classes.SelectedItem.ToString();
                this.PopulateMethods();
            }
        }

        private void btn_Browse_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg=new OpenFileDialog();
            dlg.Filter = "Assembly files(*.dll)|*.dll|All files(*.*)|*.*";
            if(dlg.ShowDialog()==System.Windows.Forms.DialogResult.OK)
            {
                this.txt_AssemblyFilePath.Text = dlg.FileName;
                this.AssemblyFilePath = this.txt_AssemblyFilePath.Text;
                this.PopulateClasses();
            }
        }

        private void PopulateClasses()
        {
            this.cbo_Classes.Items.Clear();
            this.cbo_Classes.SelectedIndex = -1;
            this.cbo_Classes.Text = string.Empty;
            if (this._Assembly != null)
            {
                Type[] types = this._Assembly.GetTypes();
                foreach (Type type in types)
                {
                    bool add = true;
                    if (this._InterfaceType != null)
                    {
                        if (type.GetInterface(this._InterfaceType.Name) == null)
                        {
                            add = false;
                        }
                    }
                    if (add)
                    {
                        this.cbo_Classes.Items.Add(type.FullName);
                    }
                }
            }
            if (this.cbo_Classes.Items.Count > 0 && (!string.IsNullOrEmpty(this._ClassName)))
            {
                for (int i = 0; i < this.cbo_Classes.Items.Count; i++)
                {
                    if (this.cbo_Classes.Items[i].ToString() == this._ClassName)
                    {
                        this.cbo_Classes.SelectedIndex = i;
                        break;
                    }
                }
            }
        }

        private void PopulateMethods()
        {
            this.cbo_Methods.Items.Clear();
            this.cbo_Methods.SelectedIndex = -1;
            this.cbo_Methods.Text = string.Empty;
            if (this._Assembly != null && (!string.IsNullOrEmpty(this._ClassName)))
            {
                Type classType = this._Assembly.GetType(this._ClassName);
                MethodInfo[] methods = classType.GetMethods();
                foreach (MethodInfo method in methods)
                {
                    if (method.IsPublic)
                    {
                        this.cbo_Methods.Items.Add(method.Name);
                    }
                }
            }
            if (this.cbo_Methods.Items.Count > 0 && (!string.IsNullOrEmpty(this._SelectedMethodName)))
            {
                for (int i = 0; i < this.cbo_Methods.Items.Count; i++)
                {
                    if (this.cbo_Methods.Items[i].ToString() == this._SelectedMethodName)
                    {
                        this.cbo_Methods.SelectedIndex = i;
                        break;
                    }
                }
            }
        }

        private void PopulateParameterBindings()
        {
            this.dv_ParameterBindings.Rows.Clear();
            Type classType = this._Assembly.GetType(this._ClassName);
            MethodInfo method = classType.GetMethod(this._SelectedMethodName);
            ParameterInfo[] paramInfos = method.GetParameters();
            if(paramInfos !=null && paramInfos.Length>0)
            {
                foreach(ParameterInfo paramInfo in paramInfos)
                {
                    DataGridViewRow dr=new DataGridViewRow();
                    DataGridViewTextBoxCell cellParamName=new DataGridViewTextBoxCell();
                    cellParamName.Value = paramInfo.Name;
                    dr.Cells.Add(cellParamName);
                    DataGridViewComboBoxCell cellBindings=new DataGridViewComboBoxCell();
                    if(this._GlobalVars !=null && this._GlobalVars.Count>0)
                    {
                        foreach(string gVarName in this._GlobalVars.Keys)
                        {
                            cellBindings.Items.Add(gVarName);
                        }
                    }
                    dr.Cells.Add(cellBindings);
                    DataGridViewCheckBoxCell cellOutput=new DataGridViewCheckBoxCell();
                    cellOutput.Value = false;
                    if(paramInfo.IsOut || paramInfo.IsRetval)
                    {
                        cellOutput.Value = true;
                    }
                    dr.Cells.Add(cellOutput);

                    this.dv_ParameterBindings.Rows.Add(dr);
                }
            }
        }
        #endregion

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btn_OK_Click(object sender, EventArgs e)
        {
            if(this.OnMethodBound !=null)
            {
                Type classType = this._Assembly.GetType(this._ClassName);
                MethodInfo method = classType.GetMethod(this._SelectedMethodName);
                Dictionary<string,string> paramBindings=new Dictionary<string, string>();
                foreach(DataGridViewRow dr in this.dv_ParameterBindings.Rows)
                {
                    if(!dr.IsNewRow && dr.Cells[0].Value !=null)
                    {
                        string paramName = dr.Cells[0].Value.ToString();
                        string gVarName = dr.Cells[1].Value.ToString();
                        paramBindings.Add(paramName, gVarName);
                    }
                }
                this.OnMethodBound(this._Assembly, classType, method, paramBindings);
            }

            this.Close();
        }
    }
}
